#include "StdAfx.h"
#include "Timer.h"
#include "Camera.h"
#include "GameObject.h"
#include "Scene.h"

#include <stdio.h>

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CMesh

CMesh::CMesh()
{
	m_pd3dxMesh = NULL;
	m_nReferences = 0;
	m_nAttributes = 1;
	m_pAttributeItemIDs = NULL;
}

CMesh::~CMesh()
{
	if (m_pd3dxMesh) m_pd3dxMesh->Release(); 
	if (m_pAttributeItemIDs) delete m_pAttributeItemIDs;
}

void CMesh::AddRef() 
{ 
	m_nReferences++; 
}

void CMesh::Release() 
{ 
	m_nReferences--; 
	if (!m_nReferences) delete this;
}



bool CMesh::LoadFromXFile(LPCTSTR lpstrFileName, DWORD dwOptions, LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
    HRESULT hResult;
	D3DMATERIAL9 d3dMaterial;
    LPD3DXBUFFER pAdjacencies;             
    LPD3DXBUFFER pAttributes;       
    LPD3DXBUFFER pEffectInstances;       
	wchar_t pstrTextureFilePath[256];

    if (!lpstrFileName || !pD3DDevice) return(false);
    if (FAILED(hResult = D3DXLoadMeshFromX(lpstrFileName, dwOptions, pD3DDevice, &pAdjacencies, &pAttributes, &pEffectInstances, &m_nAttributes, &m_pd3dxMesh))) return(false);
	DWORD *dwpAdjacencies = (DWORD *)pAdjacencies->GetBufferPointer();
	m_pd3dxMesh->OptimizeInplace(D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT, dwpAdjacencies, NULL, NULL, NULL);
    D3DXMATERIAL *pd3dxMaterials = (D3DXMATERIAL *)pAttributes->GetBufferPointer();
    m_pAttributeItemIDs = new ATTRIBUTEITEMSTRUCT[m_nAttributes];
    for (DWORD i = 0; i < m_nAttributes; i++)
    {
        d3dMaterial = pd3dxMaterials[i].MatD3D;
        d3dMaterial.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
		if(pd3dxMaterials[i].pTextureFilename == NULL)
			m_pAttributeItemIDs[i] = CScene::CollectAttributeData(pScene, NULL, &d3dMaterial);
		else {
			CStringW  FileNameW(pd3dxMaterials[i].pTextureFilename);
			swprintf_s(pstrTextureFilePath, _T("Data/%s"), FileNameW);
			m_pAttributeItemIDs[i] = CScene::CollectAttributeData(pScene, pstrTextureFilePath, &d3dMaterial);
		}
    } 
	D3DXVECTOR3 *pVertices = NULL;
	m_pd3dxMesh->LockVertexBuffer(0, (LPVOID *)&pVertices);
	if (FAILED(hResult = D3DXComputeBoundingBox(pVertices, m_pd3dxMesh->GetNumVertices(), m_pd3dxMesh->GetNumBytesPerVertex(), &m_bcBoundingCube.m_vMinimum, &m_bcBoundingCube.m_vMaximum))) return(false);
	m_pd3dxMesh->UnlockVertexBuffer();

    if (pAdjacencies) pAdjacencies->Release();
    if (pAttributes) pAttributes->Release();
    if (pEffectInstances) pEffectInstances->Release();

    return(true);
}

void CMesh::Render(LPDIRECT3DDEVICE9 pD3DDevice)
{
	if (m_pd3dxMesh) 
	{
		for (DWORD i = 0; i < m_nAttributes; i++) m_pd3dxMesh->DrawSubset(i); 
	}
}

void CMesh::Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
    int nMaterial = -1, nTexture = -1, nCurrentMaterial, nCurrentTexture;
	
    if (!m_pd3dxMesh || !pScene) return;
	for (DWORD i = 0; i < m_nAttributes; i++) 
    {
		if (m_pAttributeItemIDs)
		{
			nMaterial = m_pAttributeItemIDs[i].m_nMaterial;
			nTexture = m_pAttributeItemIDs[i].m_nTexture;
			nCurrentMaterial = pScene->GetCurrentMaterial();
			nCurrentTexture = pScene->GetCurrentTexture();
			if (nMaterial != nCurrentMaterial) 
			{
				pD3DDevice->SetMaterial((nMaterial >= 0) ? pScene->m_ppd3dMaterials[nMaterial] : &pScene->m_d3dDefaultMaterial);
				pScene->SetCurrentMaterial(nMaterial);
			}
			if (nTexture != nCurrentTexture) 
			{
				pD3DDevice->SetTexture(0, ((nTexture >= 0) ? pScene->m_ppTextureItems[nTexture]->m_pd3dTexture : pScene->m_pd3dDefaultTexture));
				pScene->SetCurrentTexture(nTexture);
			}
		}
		m_pd3dxMesh->DrawSubset(i);
    } 
}

BOOL CMesh::CheckRayIntersection(LPDIRECT3DDEVICE9 pD3DDevice, D3DXMATRIX *pmtxWorld, D3DXMATRIX *pmtxView, D3DXVECTOR3 *pvPickPosition, float *pfNearHitDistance)
{
	if (!m_pd3dxMesh) return(FALSE);

	D3DXMATRIX mtxWorldView, mtxInverse;
	mtxWorldView = (*pmtxWorld) * (*pmtxView);
    D3DXMatrixInverse(&mtxInverse, NULL, &mtxWorldView);
    D3DXVECTOR3 vPickRayDirection, vPickRayPosition;
	vPickRayDirection.x = pvPickPosition->x * mtxInverse._11 + pvPickPosition->y * mtxInverse._21 + pvPickPosition->z * mtxInverse._31;
	vPickRayDirection.y = pvPickPosition->x * mtxInverse._12 + pvPickPosition->y * mtxInverse._22 + pvPickPosition->z * mtxInverse._32;
	vPickRayDirection.z = pvPickPosition->x * mtxInverse._13 + pvPickPosition->y * mtxInverse._23 + pvPickPosition->z * mtxInverse._33;
	vPickRayPosition.x = mtxInverse._41;
	vPickRayPosition.y = mtxInverse._42;
	vPickRayPosition.z = mtxInverse._43;

	HRESULT hResult;
    LPD3DXMESH pd3dxCloneMesh;        
    if (FAILED(hResult = m_pd3dxMesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, m_pd3dxMesh->GetFVF(), pD3DDevice, &pd3dxCloneMesh))) return(false);
	float fuHitBaryCentric, fvHitBaryCentric, fHitDistance;
    BOOL bIntersected = FALSE;
    DWORD dwFace;
    D3DXIntersect(pd3dxCloneMesh, &vPickRayPosition, &vPickRayDirection, &bIntersected, &dwFace, &fuHitBaryCentric, &fvHitBaryCentric, &fHitDistance, NULL, NULL);
	pd3dxCloneMesh->Release();
	if (bIntersected && pfNearHitDistance) *pfNearHitDistance = fHitDistance;

	return(bIntersected);
}

BOOL CMesh::CheckRayIntersection(D3DXMATRIX *pmtxWorld, D3DXVECTOR3 Pos, D3DXVECTOR3 Direction, float fRayLength, float *pfNearHitDistance) {
	if (!m_pd3dxMesh) return(FALSE);

	D3DXMATRIX mtxInverse;
	D3DXMatrixInverse(&mtxInverse, NULL, pmtxWorld);
	D3DXVec3TransformCoord(&Pos, &Pos, &mtxInverse);
	D3DXVec3TransformNormal(&Direction, &Direction, &mtxInverse);

	LPDIRECT3DDEVICE9 Device = NULL;
	m_pd3dxMesh->GetDevice(&Device);
	if(Device == NULL) return FALSE;
	
	BOOL bIntersected = FALSE;    
	LPD3DXMESH pd3dxCloneMesh;        
	if (SUCCEEDED(m_pd3dxMesh->CloneMeshFVF(D3DXMESH_SYSTEMMEM, m_pd3dxMesh->GetFVF(), Device, &pd3dxCloneMesh))) {
		float fHitDistance;		
		D3DXIntersect(pd3dxCloneMesh, &Pos, &Direction, &bIntersected, NULL, NULL, NULL, &fHitDistance, NULL, NULL);		
		if (bIntersected && pfNearHitDistance) 
			if(fHitDistance <= fRayLength)
				*pfNearHitDistance = fHitDistance;	
			else
				bIntersected = FALSE;

		pd3dxCloneMesh->Release();
	}

	Device->Release();

	return(bIntersected);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CCubeMesh

CCubeMesh::CCubeMesh()
{
}

void CCubeMesh::Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions)
{
	HRESULT hResult;

	if (FAILED(hResult = D3DXCreateMeshFVF(6*2, 8, dwMeshCreateOptions, D3DFVF_XYZ | D3DFVF_DIFFUSE, pD3DDevice, &m_pd3dxMesh))) return;
    CDiffuseVertex *pVertices = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockVertexBuffer(0, (LPVOID *)&pVertices))) return;
    *pVertices++ = CDiffuseVertex(-7, -7, 0, 0xFF0000FF);
    *pVertices++ = CDiffuseVertex(-7, -7, +60, 0xFF0000FF);
    *pVertices++ = CDiffuseVertex(+7, -7, +60, 0xFF0000FF);
    *pVertices++ = CDiffuseVertex(+7, -7, 0, 0xFF0000FF);
	*pVertices++ = CDiffuseVertex(-7, +7, 0, 0xFF0000FF);
    *pVertices++ = CDiffuseVertex(-7, +7, +60, 0xFF0000FF);
    *pVertices++ = CDiffuseVertex(+7, +7, +60, 0xFF0000FF);    
    *pVertices++ = CDiffuseVertex(+7, +7, 0, 0xFF0000FF);    
    if (FAILED(hResult = m_pd3dxMesh->UnlockVertexBuffer())) return;
	USHORT *pIndices = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockIndexBuffer(0, (LPVOID *)&pIndices))) return;
	*pIndices++ = 5; //Top
	*pIndices++ = 6;
	*pIndices++ = 4;
	*pIndices++ = 6;
	*pIndices++ = 7;
	*pIndices++ = 4;
	*pIndices++ = 4; //Front
	*pIndices++ = 7;
	*pIndices++ = 0;
	*pIndices++ = 7;
	*pIndices++ = 3;
	*pIndices++ = 0;
	*pIndices++ = 0; //Bottom
	*pIndices++ = 3;
	*pIndices++ = 1;
	*pIndices++ = 3;
	*pIndices++ = 2;
	*pIndices++ = 1;
	*pIndices++ = 3; //Right
	*pIndices++ = 7;
	*pIndices++ = 2;
	*pIndices++ = 7;
	*pIndices++ = 6;
	*pIndices++ = 2;
	*pIndices++ = 2; //Back
	*pIndices++ = 6;
	*pIndices++ = 1;
	*pIndices++ = 6;
	*pIndices++ = 5;
	*pIndices++ = 1;
	*pIndices++ = 1; //Left
	*pIndices++ = 5;
	*pIndices++ = 0;
	*pIndices++ = 5;
	*pIndices++ = 4;
	*pIndices++ = 0;
    if (FAILED(hResult = m_pd3dxMesh->UnlockIndexBuffer())) return;
	DWORD *pAttribute = NULL;
	int nFaces = m_pd3dxMesh->GetNumFaces();
	if (FAILED(hResult = m_pd3dxMesh->LockAttributeBuffer(0, &pAttribute))) return;
	for (int i = 0; i < nFaces; i++) pAttribute[i] = 0;
    if (FAILED(hResult = m_pd3dxMesh->UnlockAttributeBuffer())) return;

	m_bcBoundingCube.m_vMinimum = D3DXVECTOR3(-7.0f, -7.0f, 0.0f);
	m_bcBoundingCube.m_vMaximum = D3DXVECTOR3(+7.0f, +7.0f, 60.0f);
}

void CCubeMesh::Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
	if (pD3DDevice) pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
	CMesh::Render(pD3DDevice);
}


CBoomMesh::CBoomMesh()
{
}

void CBoomMesh::Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions)
{
	HRESULT hResult;

	if (FAILED(hResult = D3DXCreateMeshFVF(6*2, 8, dwMeshCreateOptions, D3DFVF_XYZ | D3DFVF_DIFFUSE, pD3DDevice, &m_pd3dxMesh))) return;
    CDiffuseVertex *pVertices = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockVertexBuffer(0, (LPVOID *)&pVertices))) return;
    *pVertices++ = CDiffuseVertex(-28, -28, -28, 0xFFFF0000);
    *pVertices++ = CDiffuseVertex(-28, -28, +28, 0xFFFF0000);
    *pVertices++ = CDiffuseVertex(+28, -28, +28, 0xFFFF0000);
    *pVertices++ = CDiffuseVertex(+28, -28, -28, 0xFFFF0000);
	*pVertices++ = CDiffuseVertex(-28, +28, -28, 0xFFFF0000);
    *pVertices++ = CDiffuseVertex(-28, +28, +28, 0xFFFF0000);
    *pVertices++ = CDiffuseVertex(+28, +28, +28, 0xFFFF0000);    
    *pVertices++ = CDiffuseVertex(+28, +28, -28, 0xFFFF0000);    
    if (FAILED(hResult = m_pd3dxMesh->UnlockVertexBuffer())) return;
	USHORT *pIndices = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockIndexBuffer(0, (LPVOID *)&pIndices))) return;
	*pIndices++ = 5; //Top
	*pIndices++ = 6;
	*pIndices++ = 4;
	*pIndices++ = 6;
	*pIndices++ = 7;
	*pIndices++ = 4;
	*pIndices++ = 4; //Front
	*pIndices++ = 7;
	*pIndices++ = 0;
	*pIndices++ = 7;
	*pIndices++ = 3;
	*pIndices++ = 0;
	*pIndices++ = 0; //Bottom
	*pIndices++ = 3;
	*pIndices++ = 1;
	*pIndices++ = 3;
	*pIndices++ = 2;
	*pIndices++ = 1;
	*pIndices++ = 3; //Right
	*pIndices++ = 7;
	*pIndices++ = 2;
	*pIndices++ = 7;
	*pIndices++ = 6;
	*pIndices++ = 2;
	*pIndices++ = 2; //Back
	*pIndices++ = 6;
	*pIndices++ = 1;
	*pIndices++ = 6;
	*pIndices++ = 5;
	*pIndices++ = 1;
	*pIndices++ = 1; //Left
	*pIndices++ = 5;
	*pIndices++ = 0;
	*pIndices++ = 5;
	*pIndices++ = 4;
	*pIndices++ = 0;
    if (FAILED(hResult = m_pd3dxMesh->UnlockIndexBuffer())) return;
	DWORD *pAttribute = NULL;
	int nFaces = m_pd3dxMesh->GetNumFaces();
	if (FAILED(hResult = m_pd3dxMesh->LockAttributeBuffer(0, &pAttribute))) return;
	for (int i = 0; i < nFaces; i++) pAttribute[i] = 0;
    if (FAILED(hResult = m_pd3dxMesh->UnlockAttributeBuffer())) return;

	m_bcBoundingCube.m_vMinimum = D3DXVECTOR3(-28.0f, -28.0f, -28.0f);
	m_bcBoundingCube.m_vMaximum = D3DXVECTOR3(+28.0f, +28.0f, +28.0f);
}

void CBoomMesh::Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
	if (pD3DDevice) pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
	CMesh::Render(pD3DDevice);
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CSpaceShipMesh

CSpaceShipMesh::CSpaceShipMesh()
{
}

void CSpaceShipMesh::Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
	if (pD3DDevice) pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    pD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
	CMesh::Render(pD3DDevice, pScene);
}

void CSpaceShipMesh::Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions)
{
	LoadFromXFile(_T("Data/SpaceShip.x"), dwMeshCreateOptions, pD3DDevice, pScene);

	D3DXVECTOR3 *pVertices = NULL;
	m_pd3dxMesh->LockVertexBuffer(0, (LPVOID *)&pVertices);
	D3DXComputeBoundingBox(pVertices, m_pd3dxMesh->GetNumVertices(), m_pd3dxMesh->GetNumBytesPerVertex(), &m_bcBoundingCube.m_vMinimum, &m_bcBoundingCube.m_vMaximum);
	m_pd3dxMesh->UnlockVertexBuffer();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CAsteroidMesh

CAsteroidMesh::CAsteroidMesh()
{
}

void CAsteroidMesh::Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
	if (pD3DDevice) pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    pD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
	CMesh::Render(pD3DDevice, pScene);
}

void CAsteroidMesh::Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions)
{
	LoadFromXFile(_T("Data/Asteroid.x"), dwMeshCreateOptions, pD3DDevice, pScene);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CCruiser01Mesh

CCruiser01Mesh::CCruiser01Mesh()
{
}

void CCruiser01Mesh::Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
	if (pD3DDevice) pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    pD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
	CMesh::Render(pD3DDevice, pScene);
}

void CCruiser01Mesh::Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions)
{
	LoadFromXFile(_T("Data/Cruiser01.x"), dwMeshCreateOptions, pD3DDevice, pScene);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CCruiser02Mesh

CCruiser02Mesh::CCruiser02Mesh()
{
}

void CCruiser02Mesh::Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
	if (pD3DDevice) pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    pD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
	CMesh::Render(pD3DDevice, pScene);
}

void CCruiser02Mesh::Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions)
{
	LoadFromXFile(_T("Data/Cruiser02.x"), dwMeshCreateOptions, pD3DDevice, pScene);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CSkyBoxMesh

CSkyBoxMesh::CSkyBoxMesh(void)
{
}

CSkyBoxMesh::~CSkyBoxMesh(void)
{
}

void CSkyBoxMesh::Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions)
{
	HRESULT hResult; 

	if (FAILED(hResult = D3DXCreateMeshFVF(6*2, 4*6, dwMeshCreateOptions, FVF_TEXTURE_1_SET, pD3DDevice, &m_pd3dxMesh))) return;
    CTexturedVertex *pVertices = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockVertexBuffer(0, (LPVOID *)&pVertices))) return;
    // Front quad (remember all quads point inward)
    *pVertices++ = CTexturedVertex(-10.0f, +10.0f, +10.0f, 0.0f, 0.0f);
    *pVertices++ = CTexturedVertex(+10.0f, +10.0f, +10.0f, 1.0f, 0.0f);
    *pVertices++ = CTexturedVertex(+10.0f, -10.0f, +10.0f, 1.0f, 1.0f);
    *pVertices++ = CTexturedVertex(-10.0f, -10.0f, +10.0f, 0.0f, 1.0f);
    // Back Quad
    *pVertices++ = CTexturedVertex(+10.0f, +10.0f, -10.0f, 0.0f, 0.0f);
    *pVertices++ = CTexturedVertex(-10.0f, +10.0f, -10.0f, 1.0f, 0.0f);
    *pVertices++ = CTexturedVertex(-10.0f, -10.0f, -10.0f, 1.0f, 1.0f);
    *pVertices++ = CTexturedVertex(+10.0f, -10.0f, -10.0f, 0.0f, 1.0f);
    // Left Quad
    *pVertices++ = CTexturedVertex(-10.0f, +10.0f, -10.0f, 0.0f, 0.0f);
    *pVertices++ = CTexturedVertex(-10.0f, +10.0f, +10.0f, 1.0f, 0.0f);
    *pVertices++ = CTexturedVertex(-10.0f, -10.0f, +10.0f, 1.0f, 1.0f);
    *pVertices++ = CTexturedVertex(-10.0f, -10.0f, -10.0f, 0.0f, 1.0f);
    // Right Quad
    *pVertices++ = CTexturedVertex(+10.0f, +10.0f, +10.0f, 0.0f, 0.0f);
    *pVertices++ = CTexturedVertex(+10.0f, +10.0f, -10.0f, 1.0f, 0.0f);
    *pVertices++ = CTexturedVertex(+10.0f, -10.0f, -10.0f, 1.0f, 1.0f);
    *pVertices++ = CTexturedVertex(+10.0f, -10.0f, +10.0f, 0.0f, 1.0f);
    // Top Quad
    *pVertices++ = CTexturedVertex(-10.0f, +10.0f, -10.0f, 0.0f, 0.0f);
    *pVertices++ = CTexturedVertex(+10.0f, +10.0f, -10.0f, 1.0f, 0.0f);
    *pVertices++ = CTexturedVertex(+10.0f, +10.0f, +10.0f, 1.0f, 1.0f);
    *pVertices++ = CTexturedVertex(-10.0f, +10.0f, +10.0f, 0.0f, 1.0f);
    // Bottom Quad
    *pVertices++ = CTexturedVertex(-10.0f, -10.0f, +10.0f, 0.0f, 0.0f);
    *pVertices++ = CTexturedVertex(+10.0f, -10.0f, +10.0f, 1.0f, 0.0f);
    *pVertices++ = CTexturedVertex(+10.0f, -10.0f, -10.0f, 1.0f, 1.0f);
    *pVertices++ = CTexturedVertex(-10.0f, -10.0f, -10.0f, 0.0f, 1.0f);
    if (FAILED(hResult = m_pd3dxMesh->UnlockVertexBuffer())) return;

	USHORT *pIndices = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockIndexBuffer(0, (LPVOID *)&pIndices))) return;
    *pIndices++ = 0; *pIndices++ = 1; *pIndices++ = 3;
    *pIndices++ = 1; *pIndices++ = 2; *pIndices++ = 3;
    *pIndices++ = 4; *pIndices++ = 5; *pIndices++ = 7;
    *pIndices++ = 5; *pIndices++ = 6; *pIndices++ = 7;
    *pIndices++ = 8; *pIndices++ = 9; *pIndices++ = 11;
    *pIndices++ = 9; *pIndices++ = 10; *pIndices++ = 11;
    *pIndices++ = 12; *pIndices++ = 13; *pIndices++ = 15;
    *pIndices++ = 13; *pIndices++ = 14; *pIndices++ = 15;
    *pIndices++ = 16; *pIndices++ = 17; *pIndices++ = 19;
    *pIndices++ = 17; *pIndices++ = 18; *pIndices++ = 19;
    *pIndices++ = 20; *pIndices++ = 21; *pIndices++ = 23;
    *pIndices++ = 21; *pIndices++ = 22; *pIndices++ = 23;
    if (FAILED(hResult = m_pd3dxMesh->UnlockIndexBuffer())) return;

	m_nAttributes = 6;
	DWORD *pdwAttributes = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockAttributeBuffer(0, &pdwAttributes))) return;
	for (int i = 0; i < 6; i++) pdwAttributes[i*2] = pdwAttributes[i*2+1] = i;
    if (FAILED(hResult = m_pd3dxMesh->UnlockAttributeBuffer())) return;

    D3DMATERIAL9 *pd3dMaterial = new D3DMATERIAL9;
    ZeroMemory(pd3dMaterial, sizeof(D3DMATERIAL9));
    pd3dMaterial->Diffuse = D3DXCOLOR(1.0, 1.0, 1.0, 1.0f);
    pd3dMaterial->Ambient = D3DXCOLOR(1.0, 1.0, 1.0, 1.0f);
    int nMaretial = pScene->AddMaterial(pd3dMaterial); 

    m_pAttributeItemIDs = new ATTRIBUTEITEMSTRUCT[m_nAttributes];
	_TCHAR pstrSkyBoxTextureFileNames[6][256];
	_tcscpy_s(pstrSkyBoxTextureFileNames[0], 256, _T("Data/SkyBox/SkyBox_Front.jpg"));
	_tcscpy_s(pstrSkyBoxTextureFileNames[1], 256, _T("Data/SkyBox/SkyBox_Back.jpg"));
	_tcscpy_s(pstrSkyBoxTextureFileNames[2], 256, _T("Data/SkyBox/SkyBox_Left.jpg"));
	_tcscpy_s(pstrSkyBoxTextureFileNames[3], 256, _T("Data/SkyBox/SkyBox_Right.jpg"));
	_tcscpy_s(pstrSkyBoxTextureFileNames[4], 256, _T("Data/SkyBox/SkyBox_Top.jpg"));
	_tcscpy_s(pstrSkyBoxTextureFileNames[5], 256, _T("Data/SkyBox/SkyBox_Bottom.jpg"));
    for (DWORD i = 0; i < 6; i++)
    {
		m_pAttributeItemIDs[i].m_nTexture = CScene::CollectTexture(pScene, pstrSkyBoxTextureFileNames[i], 1);
		m_pAttributeItemIDs[i].m_nMaterial = nMaretial;  
    } 
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CSkyBox

CSkyBox::CSkyBox(void)
{
}

CSkyBox::~CSkyBox(void)
{
}

void CSkyBox::Build(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene, DWORD dwMeshCreateOptions)
{
	CSkyBoxMesh *pSkyBoxMesh = new CSkyBoxMesh();
	pSkyBoxMesh->Build(pD3DDevice, pScene, dwMeshCreateOptions);
	SetMesh(pSkyBoxMesh);
}

void CSkyBox::Render(LPDIRECT3DDEVICE9 pD3DDevice, CCamera *pCamera, CScene *pScene)
{
	D3DXVECTOR3 vCameraPos(0.0f, 0.0f, 0.0f); 
	if (pCamera) vCameraPos = pCamera->GetPosition();
    D3DXMATRIX mtxWorld;
    D3DXMatrixTranslation(&mtxWorld, vCameraPos.x, (vCameraPos.y + 1.3f), vCameraPos.z);
    pD3DDevice->SetTransform(D3DTS_WORLD, &mtxWorld);
    pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
    pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    pD3DDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
    pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
    pD3DDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
	m_pMesh->Render(pD3DDevice, pScene);
	pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
    pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
    pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
//    pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_DISABLE);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CGameObject

CGameObject::CGameObject()
{
    m_pMesh = NULL;
    D3DXMatrixIdentity(&m_mtxWorld);
	m_fPitch = 0.0f;             
    m_fRoll = 0.0f;              
    m_fYaw = 0.0f;               
}

CGameObject::CGameObject(CMesh *pMesh)
{
    m_pMesh = pMesh;
    if (m_pMesh) m_pMesh->AddRef();
    D3DXMatrixIdentity(&m_mtxWorld);
}

CGameObject::~CGameObject(void)
{
	if (m_pMesh) m_pMesh->Release();
}

void CGameObject::SetMesh(CMesh *pMesh)
{
    if (m_pMesh) m_pMesh->Release();
    m_pMesh = pMesh;
    if (m_pMesh) m_pMesh->AddRef();
}

void CGameObject::Render(LPDIRECT3DDEVICE9 pD3DDevice)
{
	if (pD3DDevice)
	{
		pD3DDevice->SetTransform(D3DTS_WORLD, &m_mtxWorld);
		if (m_pMesh) m_pMesh->Render(pD3DDevice);
	}
}

void CGameObject::Render(LPDIRECT3DDEVICE9 pD3DDevice, CCamera *pCamera)
{
	if (pD3DDevice && m_pMesh)
	{
		D3DXVECTOR3 vMinimum, vMaximum;		
        D3DXVec3TransformCoord(&vMinimum, &m_pMesh->m_bcBoundingCube.m_vMinimum, &m_mtxWorld);
        D3DXVec3TransformCoord(&vMaximum, &m_pMesh->m_bcBoundingCube.m_vMaximum, &m_mtxWorld);		
		if (pCamera && pCamera->IsInFrustum(vMinimum, vMaximum))
		{
			pD3DDevice->SetTransform(D3DTS_WORLD, &m_mtxWorld);
			m_pMesh->Render(pD3DDevice);
		}
	}
}

void CGameObject::Render(LPDIRECT3DDEVICE9 pD3DDevice, CCamera *pCamera, CScene *pScene)
{
	if (pD3DDevice && m_pMesh)
	{
		//D3DXVECTOR3 vMinimum, vMaximum;
  //      D3DXVec3TransformCoord(&vMinimum, &m_pMesh->m_bcBoundingCube.m_vMinimum, &m_mtxWorld);
  //      D3DXVec3TransformCoord(&vMaximum, &m_pMesh->m_bcBoundingCube.m_vMaximum, &m_mtxWorld);
		//if (pCamera && pCamera->IsInFrustum(vMinimum, vMaximum))
		//{
			pD3DDevice->SetTransform(D3DTS_WORLD, &m_mtxWorld);
			m_pMesh->Render(pD3DDevice, pScene);
//		}
	}
}

void CGameObject::MoveForward(float fDistance)
{
	D3DXVECTOR3 vLook;
	vLook.x = m_mtxWorld._31;
	vLook.y = m_mtxWorld._32;
	vLook.z = m_mtxWorld._33;
	D3DXVec3Normalize(&vLook, &vLook);
	m_mtxWorld._41 += (vLook.x * fDistance);
	m_mtxWorld._42 += (vLook.y * fDistance);
	m_mtxWorld._43 += (vLook.z * fDistance);
}

void CGameObject::MoveStrafe(float fDistance)
{
	D3DXVECTOR3 vRight;
	vRight.x = m_mtxWorld._11;
	vRight.y = m_mtxWorld._12;
	vRight.z = m_mtxWorld._13;
	D3DXVec3Normalize(&vRight, &vRight);
	m_mtxWorld._41 += (vRight.x * fDistance);
	m_mtxWorld._42 += (vRight.y * fDistance);
	m_mtxWorld._43 += (vRight.z * fDistance);
}

void CGameObject::MoveUpDown(float fDistance)
{
	D3DXVECTOR3 vUp;
	vUp.x = m_mtxWorld._21;
	vUp.y = m_mtxWorld._22;
	vUp.z = m_mtxWorld._23;
	D3DXVec3Normalize(&vUp, &vUp);
	m_mtxWorld._41 += (vUp.x * fDistance);
	m_mtxWorld._42 += (vUp.y * fDistance);
	m_mtxWorld._43 += (vUp.z * fDistance);
}

void CGameObject::Rotate(float x, float y, float z)
{
	m_fPitch += x; if (m_fPitch > 360.0f) m_fPitch -= 360.0f;
	m_fYaw += y; if (m_fYaw > 360.0f) m_fYaw -= 360.0f;
	m_fRoll += z; if (m_fRoll > 360.0f) m_fRoll -= 360.0f;
	D3DXMATRIX mtxRotate;
	D3DXMatrixRotationYawPitchRoll(&mtxRotate, D3DXToRadian(x), D3DXToRadian(y), D3DXToRadian(z));
    D3DXMatrixMultiply(&m_mtxWorld, &mtxRotate, &m_mtxWorld);
}





///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CGridMesh

CGridMesh::CGridMesh(int xStart, int zStart, int nWidth, int nHeight)
{
	m_xStart = xStart;
	m_zStart = zStart;
    m_nWidth = nWidth;
    m_nHeight = nHeight;
    m_cxQuads = m_nWidth - 1;
    m_czQuads = m_nHeight - 1;
}

CGridMesh::CGridMesh()
{
}

CGridMesh::~CGridMesh()
{
}

void CGridMesh::Build(LPDIRECT3DDEVICE9 pD3DDevice, CHeightMap *pHeightMap, DWORD dwMeshCreateOptions)
{
	if (!pHeightMap) return;

	HRESULT hResult;
	int x, z, nCount = 0;
	D3DXVECTOR3 vPosition;
	D3DXVECTOR3 vLightDir = -D3DXVECTOR3(0.650945f, -0.390567f, 0.650945f);
	float fRed = 1.0f, fGreen = 0.8f, fBlue = 0.6f, fScale = 1.05f;

	float MapxUnit = 1.0f / pHeightMap->GetHeightMapWidth();
	float MapzUnit = 1.0f / pHeightMap->GetHeightMapHeight();

	int nVertices = (m_nWidth * m_nHeight);
	int nPrimitives = m_czQuads * m_cxQuads * 2;
	

	if (FAILED(hResult = D3DXCreateMeshFVF(nPrimitives, nVertices, dwMeshCreateOptions, FVF_NORMAL_TEX2, pD3DDevice, &m_pd3dxMesh))) return;
    CNormalTexture2 *pVertices = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockVertexBuffer(0, (LPVOID *)&pVertices))) return;
	BYTE *pHeightMapImage = pHeightMap->GetHeightMapImage();
	D3DXVECTOR3 vScale = pHeightMap->GetScale();
	int cxTerrain = pHeightMap->GetHeightMapWidth();
	for (z = m_zStart; z < (m_zStart+m_nHeight); z++)
    {
        for (x = m_xStart; x < (m_xStart+m_nWidth); x++)
        {
			pVertices->m_vPosition = D3DXVECTOR3((x*vScale.x), pHeightMapImage[x+(z*cxTerrain)]*vScale.y, (z*vScale.z));
			pVertices->m_vNormal = pHeightMap->GetHeightMapNormal(x, z);		
			pVertices->m_Tex1 = D3DXVECTOR2(x * MapxUnit, z * MapzUnit);
			pVertices->m_Tex2 = D3DXVECTOR2(x, z);

			++pVertices;
		} 
    }     
    if (FAILED(hResult = m_pd3dxMesh->UnlockVertexBuffer())) return;

	
	USHORT *pIndices = NULL;
	if (FAILED(hResult = m_pd3dxMesh->LockIndexBuffer(0, (LPVOID *)&pIndices))) return;
	for(int z = 0; z < m_czQuads; ++z) 
		for(int x = 0; x < m_cxQuads; ++x) {
			*pIndices++ = x		+ ( z * m_nWidth );
			*pIndices++ = x		+ ( (z+1) * m_nWidth);
			*pIndices++ = (x+1) + ( z * m_nWidth);

			*pIndices++ = x		+ ( (z+1) * m_nWidth);
			*pIndices++ = (x+1) + ( (z+1) * m_nWidth);
			*pIndices++ = (x+1) + ( z * m_nWidth);
		}		
    if (FAILED(hResult = m_pd3dxMesh->UnlockIndexBuffer())) return;


	DWORD *pAttribute = NULL;
	int nFaces = m_pd3dxMesh->GetNumFaces();
	if (FAILED(hResult = m_pd3dxMesh->LockAttributeBuffer(0, &pAttribute))) return;
	for (int i = 0; i < nFaces; i++) pAttribute[i] = 0;
    if (FAILED(hResult = m_pd3dxMesh->UnlockAttributeBuffer())) return;
}

void CGridMesh::Render(LPDIRECT3DDEVICE9 pD3DDevice)
{
//	pD3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
	CMesh::Render(pD3DDevice);
}



CHeightMap::CHeightMap()
{
	m_pHeightMapImage = NULL;
}

CHeightMap::~CHeightMap()
{
	if (m_pHeightMapImage) delete [] m_pHeightMapImage;
}

bool CHeightMap::LoadHeightMap(LPCTSTR pFileName, int nWidth, int nHeight)
{
    m_nWidth = nWidth;
    m_nHeight = nHeight;

    m_vScale.x = 24.0f * (512 / (m_nWidth - 1));
    m_vScale.y = 2.0f;
    m_vScale.z = 24.0f * (512 / (m_nHeight - 1));

    m_pHeightMapImage = new BYTE[m_nWidth * m_nHeight];
    if (!m_pHeightMapImage) return(false);

	HANDLE hFile = ::CreateFile(pFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_READONLY, NULL);
    if (hFile == INVALID_HANDLE_VALUE) return(false);
	DWORD dwBytesRead;
	::ReadFile(hFile, m_pHeightMapImage, (m_nWidth * m_nHeight), &dwBytesRead, NULL);
	::CloseHandle(hFile);

    return(true);
}

D3DXVECTOR3 CHeightMap::GetHeightMapNormal(int x, int z)
{
	if ((x < 0.0f) || (z < 0.0f) || (x >= m_nWidth) || (z >= m_nHeight)) return(D3DXVECTOR3(0.0f, 1.0f, 0.0f));
    int nHeightMapIndex = x + (z * m_nWidth);
	int xHeightMapAdd, zHeightMapAdd;
	xHeightMapAdd = (x < (m_nWidth - 1)) ? 1 : -1;
	zHeightMapAdd = (z < (m_nHeight - 1)) ? m_nWidth : -(signed)m_nWidth;
	float y1 = (float)m_pHeightMapImage[nHeightMapIndex] * m_vScale.y;
	float y2 = (float)m_pHeightMapImage[nHeightMapIndex + xHeightMapAdd] * m_vScale.y; 
	float y3 = (float)m_pHeightMapImage[nHeightMapIndex + zHeightMapAdd] * m_vScale.y;
	D3DXVECTOR3 vEdge1 = D3DXVECTOR3(0.0f, y3 - y1, m_vScale.z);
	D3DXVECTOR3 vEdge2 = D3DXVECTOR3(m_vScale.x, y2 - y1, 0.0f);
	D3DXVECTOR3 vNormal;
	D3DXVec3Cross(&vNormal, &vEdge1, &vEdge2);
	D3DXVec3Normalize(&vNormal, &vNormal);
	return(vNormal);
}

float CHeightMap::GetHeight(float fx, float fz, bool bReverseQuad)
{
    fx = fx / m_vScale.x;
    fz = fz / m_vScale.z;
	if ((fx < 0.0f) || (fz < 0.0f) || (fx >= m_nWidth) || (fz >= m_nHeight)) return(0.0f);
    int x = (int)fx;
    int z = (int)fz;
    float fxPercent = fx - x;
    float fzPercent = fz - z;

    float fTopLeft, fTopRight, fBottomLeft, fBottomRight;
    if (bReverseQuad)
    {
        fTopLeft = (float)m_pHeightMapImage[x+(z*m_nWidth)] * m_vScale.y;
        fBottomRight = (float)m_pHeightMapImage[(x+1)+((z+1)*m_nWidth)] * m_vScale.y;
        if (fxPercent < fzPercent)
        {
            fBottomLeft = (float)m_pHeightMapImage[x+((z+1)*m_nWidth)] * m_vScale.y;
		    fTopRight = fTopLeft + (fBottomRight - fBottomLeft);        
        } 
        else
        {
            fTopRight = (float)m_pHeightMapImage[(x+1)+(z*m_nWidth)] * m_vScale.y;
		    fBottomLeft = fTopLeft + (fBottomRight - fTopRight);
        } 
    } 
    else
    {
        fTopRight = (float)m_pHeightMapImage[(x+1)+(z*m_nWidth)] * m_vScale.y;
        fBottomLeft =(float)m_pHeightMapImage[x+((z+1)*m_nWidth)] * m_vScale.y;
        if (fxPercent < (1.0f - fzPercent)) 
        {
            fTopLeft = (float)m_pHeightMapImage[x+(z*m_nWidth)] * m_vScale.y;
            fBottomRight = fBottomLeft + (fTopRight - fTopLeft);        
        } 
        else
        {
            fBottomRight =(float)m_pHeightMapImage[(x+1)+((z+1)*m_nWidth)] * m_vScale.y;
            fTopLeft = fTopRight + (fBottomLeft - fBottomRight);
        } 
    } 
    float fTopHeight = fTopLeft + ((fTopRight - fTopLeft) * fxPercent);
    float fBottomHeight = fBottomLeft + ((fBottomRight - fBottomLeft) * fxPercent);
    return(fTopHeight + ((fBottomHeight - fTopHeight) * fzPercent));
}

D3DXVECTOR3 CHeightMap::GetHeightMapNormal(float fx, float fz) {
	fx = fx / m_vScale.x;
    fz = fz / m_vScale.z;
	if ((fx < 0.0f) || (fz < 0.0f) || (fx >= m_nWidth) || (fz >= m_nHeight)) return D3DXVECTOR3(0.0f, 1.0f, 0.0f);
    int x = (int)fx;
    int z = (int)fz;
    float fxPercent = fx - x;
    float fzPercent = fz - z;	

	D3DXVECTOR3 TopLeft = GetHeightMapNormal(x, z+1);
	D3DXVECTOR3 TopRight = GetHeightMapNormal(x+1, z+1);
	D3DXVECTOR3 BottomLeft = GetHeightMapNormal(x, z);
	D3DXVECTOR3 BottomRight = GetHeightMapNormal(x+1, z);

	D3DXVECTOR3 Top(TopLeft.x * fxPercent + TopRight.x * (1.0f - fxPercent),
					TopLeft.y * fxPercent + TopRight.y * (1.0f - fxPercent),
					TopLeft.z * fxPercent + TopRight.z * (1.0f - fxPercent));
	D3DXVECTOR3 Bottom(BottomLeft.x * fxPercent + BottomRight.x * (1.0f - fxPercent),
					BottomLeft.y * fxPercent + BottomRight.y * (1.0f - fxPercent),
					BottomLeft.z * fxPercent + BottomRight.z * (1.0f - fxPercent));

	D3DXVECTOR3 Result(Top.x * fzPercent + Bottom.x * (1.0f - fzPercent),
					Top.y * fzPercent + Bottom.y * (1.0f - fzPercent),
					Top.z * fzPercent + Bottom.z * (1.0f - fzPercent));
	D3DXVec3Normalize(&Result, &Result);
	return Result;
}



CTerrain::CTerrain(void) {
    m_cxTerrain = 0;
    m_czTerrain = 0;
    m_ppMesh = NULL;
	m_nMeshes = 0;
    D3DXMatrixIdentity(&m_mtxWorld);

	

}

CTerrain::~CTerrain(void)
{
    if (m_ppMesh) 
    {
        for(int i = 0; i < m_nMeshes; i++) if (m_ppMesh[i]) delete m_ppMesh[i];
        delete [] m_ppMesh;    
    } 
}

void CTerrain::Build(CScene *pScene, LPDIRECT3DDEVICE9 pD3DDevice, DWORD dwUsage, D3DPOOL dwMemoryPool)
{
	m_cxTerrain = 257;
	m_czTerrain = 257;
	m_nBlockWidth = 17;
	m_nBlockHeight = 17;
	m_cxQuadsPerBlock = m_nBlockWidth - 1;
	m_czQuadsPerBlock = m_nBlockHeight - 1;
	if (!m_HeightMap.LoadHeightMap(_T("Data\\HeightMap.raw"), m_cxTerrain, m_czTerrain)) return;
    long cxBlocks = (m_cxTerrain - 1) / m_cxQuadsPerBlock;
    long czBlocks = (m_czTerrain - 1) / m_czQuadsPerBlock;
	m_nMeshes = cxBlocks * czBlocks;
	m_ppMesh = new CGridMesh*[m_nMeshes];

	CGridMesh *pGridMesh = NULL;
	long x, z, xStart, zStart;
    for(z = 0; z < czBlocks; z++)
    {
        for(x = 0; x < cxBlocks; x++)
        {
            xStart  = x * (m_nBlockWidth  - 1);
            zStart  = z * (m_nBlockHeight - 1);
			m_ppMesh[x+(z*cxBlocks)] = pGridMesh = new CGridMesh(xStart, zStart, m_nBlockWidth, m_nBlockHeight);
			pGridMesh->Build(pD3DDevice, &m_HeightMap);
        } 
    } 

	D3DMATERIAL9 Material;
	Material.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); 
	Material.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); 
	Material.Specular = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f); 
	Material.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f); 	
	Material.Power = 0.0f;

	m_Attrubute[0] = CScene::CollectAttributeData(pScene, _T("Data/Base_Texture(Original).jpg"), &Material);
	m_Attrubute[1] = CScene::CollectAttributeData(pScene, _T("Data/Detail_Texture_0.JPG"), &Material);

	

	//D3DXCreateTextureFromFile(pD3DDevice, _T("Base_Texture(Original).jpg"), &this->m_MapTex);
	//D3DXCreateTextureFromFile(pD3DDevice, _T("Detail_Texture_0.JPG"), &this->m_DetailTex);
	//
	//this->m_Material.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); 
	//this->m_Material.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f); 
	//this->m_Material.Specular = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f); 
	//this->m_Material.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f); 	
	//this->m_Material.Power = 0.0f;
}

void CTerrain::Render(LPDIRECT3DDEVICE9 pD3DDevice, CScene *pScene)
{
	pD3DDevice->SetFVF(FVF_NORMAL_TEX2);
	//pD3DDevice->SetMaterial(&this->m_Material);	
	pD3DDevice->SetMaterial(pScene->m_ppd3dMaterials[m_Attrubute[0].m_nMaterial]);
	pD3DDevice->SetTransform(D3DTS_WORLD, &m_mtxWorld);

	pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);

	//pD3DDevice->SetTexture(0, this->m_MapTex);
	//pD3DDevice->SetTexture(1, this->m_DetailTex);
	pD3DDevice->SetTexture(0, pScene->m_ppTextureItems[m_Attrubute[0].m_nTexture]->m_pd3dTexture);
	pD3DDevice->SetTexture(1, pScene->m_ppTextureItems[m_Attrubute[1].m_nTexture]->m_pd3dTexture);
	pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE);
	pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1,   D3DTA_TEXTURE);
	pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2,   D3DTA_CURRENT);
	pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
	pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
	pD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_MODULATE);
	pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG1,   D3DTA_TEXTURE);
	pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG2,   D3DTA_CURRENT);
	pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE);



	for (int i = 0; i < m_nMeshes; i++) m_ppMesh[i]->Render(pD3DDevice);

	pD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE);
	pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE);

	pD3DDevice->SetTexture(0, NULL);
}

void CTerrain::UpdatePlayerCallback(LPVOID pContext, CPlayer *pPlayer, float fTimeScale)
{
    if (!pContext || !pPlayer) return;

	CTerrain *pTerrain = (CTerrain *)pContext;
	D3DXVECTOR3 vScale = pTerrain->m_HeightMap.GetScale();
    D3DXVECTOR3 vPosition = pPlayer->GetPosition();
	BOUNDINGCUBE bcBoundingCube = pPlayer->GetBoundingCube();
    int z = (int)(vPosition.z / vScale.z);
    bool bReverseQuad = ((z % 2) != 0);
	float fHeight = pTerrain->m_HeightMap.GetHeight(vPosition.x, vPosition.z, bReverseQuad) - bcBoundingCube.m_vMinimum.y;
	fHeight += pPlayer->GetFloat();
	
    if (vPosition.y < fHeight)
    {
		D3DXVECTOR3 vVelocity = pPlayer->GetVelocity();
        vVelocity.y = 0;
        vPosition.y = fHeight;
        pPlayer->SetVelocity(vVelocity);
        pPlayer->SetPosition(vPosition);
    } 
}

void CTerrain::UpdateCameraCallback(LPVOID pContext, CCamera *pCamera, float fTimeScale)
{
    if (!pContext || !pCamera) return;
    if (pCamera->GetCameraMode() != THIRD_PERSON_CAMERA) return;

	CTerrain *pTerrain = (CTerrain *)pContext;
	D3DXVECTOR3 vScale = pTerrain->m_HeightMap.GetScale();
    D3DXVECTOR3 vPosition = pCamera->GetPosition();
	BOUNDINGCUBE bcBoundingCube = pCamera->GetBoundingCube();
    int z = (int)(vPosition.z / vScale.z);
    bool bReverseQuad = ((z % 2) != 0);
    float fHeight = pTerrain->m_HeightMap.GetHeight(vPosition.x, vPosition.z, bReverseQuad) - bcBoundingCube.m_vMinimum.y;

    if (vPosition.y < fHeight)
    {
        vPosition.y = fHeight;
        pCamera->SetPosition(vPosition);
    } 
    CPlayer *pPlayer = pCamera->GetPlayer();
    if (pPlayer) ((CThirdPersonCamera *)pCamera)->SetLookAt(pPlayer->GetPosition());
}
